package kmdm.security

import org.bson.codecs.configuration.CodecRegistries.fromCodecs
import org.bson.codecs.configuration.CodecRegistries.fromRegistries
import org.bson.codecs.configuration.CodecRegistry
import org.bson.types.ObjectId
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY
import org.mongodb.scala.bson.conversions.Bson
import org.mongodb.scala.Completed
import org.mongodb.scala.MongoClient
import org.mongodb.scala.MongoCollection
import org.mongodb.scala.MongoDatabase
import play.api.Configuration
import play.api.inject.Injector

import scala.concurrent.duration.DurationInt
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.Await
import scala.concurrent.Future
import scala.reflect.ClassTag

/**
  * Created by fanqiang on 17/3/17.
  * Modified by deanzhang on 17/8/12
  */
trait AbstractDao {
  protected def dbName: String
  protected def collectionName: String
}

abstract class AbstractDaoImpl[T <: AnyRef: ClassTag](implicit injector: Injector) extends AbstractDao {
  val log = org.slf4j.LoggerFactory.getLogger(this.getClass)
  lazy val mongodb: MongoClient = injector.instanceOf(classOf[MongoClient])
  lazy val configuration: Configuration = injector.instanceOf(classOf[Configuration])
  def dbName: String = configuration.underlying.getString("elemental.mongo.dbName")
  def getDataBase: MongoDatabase = mongodb.getDatabase(dbName)
  val queryTimeout: FiniteDuration = 60.second


  protected val defaultRegistry: CodecRegistry = fromRegistries(
    DEFAULT_CODEC_REGISTRY
  )


  protected def getCollection(implicit codecRegistry: CodecRegistry): MongoCollection[T] =
    getDataBase.getCollection[T](collectionName).withCodecRegistry(codecRegistry)

  protected def getCollectionWithNewType[A <: AnyRef: ClassTag](implicit codecRegistry: CodecRegistry): MongoCollection[A] =
    getDataBase.getCollection[A](collectionName).withCodecRegistry(codecRegistry)

  protected implicit def await[A](future: Future[A]): A = Await.result(future, queryTimeout)

  def getAll(implicit codecRegistry: CodecRegistry): Seq[T] = getCollection.find().toFuture()

  def get(id: String)(implicit codecRegistry: CodecRegistry): Option[T] = get(new ObjectId(id))

  def get(id: ObjectId)(implicit codecRegistry: CodecRegistry): Option[T] =
    getCollection.find(org.mongodb.scala.model.Filters.equal("_id", id)).toFuture().headOption

  def find(filter: Bson)(implicit codecRegistry: CodecRegistry): Seq[T] = getCollection.find(filter).toFuture()

  def find(filter: Bson, sort: Bson)(implicit codecRegistry: CodecRegistry): Seq[T] = getCollection.find(filter).sort(sort).toFuture()

  def findOne(filter: Bson)(implicit codecRegistry: CodecRegistry): Option[T] = getCollection.find(filter).toFuture().headOption

  /*def save(t: T)(implicit codecRegistry: CodecRegistry): Unit = {
    log.info(s"save entity. className: ${this.getClass.getName}, document: $t")
    getCollection.insertOne(t).subscribe(new Observer[Completed] {
      override def onError(e: Throwable): Unit = log.error("save error!", e)
      override def onComplete(): Unit = log.info("save complete!")
      override def onNext(result: Completed): Unit = log.info("save onNext!")
    })
  }*/
  //为什么限定为protected和final，自行体会
  protected final def save(t: T)(implicit codecRegistry: CodecRegistry): Future[Completed] = {
    log.info(s"save entity. className: ${this.getClass.getName}, document: $t")
    getCollection.insertOne(t).toFuture
  }
}
